// QSFGUI.C - "cosmic recursive fractal flames"
// GNU version by Scott Draves <http://scottdraves.com> Copyright (C) 1992
// W95 version by Michael Sargent <msargent@zoo.uvm.edu> Copyright (C) 1999
// Revisions by Michael Sargent <msargent@uvm.edu> Copyright (C) 2018
/*=========================================================================
   This is unsupported, non-standard software. It is provided "as is" and
   any express or implied warranties, including, but not limited to, the
   implied warranties of merchantability and fitness for a particular
   purpose are disclaimed. In no event shall the author be liable for any
   direct, indirect, incidental, special, exemplary, or consequential
   damages (including, but not limited to, procurement of substitute goods
   or services; loss of use, data or profits; or business interruption)
   however caused and on any theory of liablility, whether in contract,
   strict liability, or tort (including negligence or otherwise) arising in
   any way out of the use of this software, even if advised of the
   possibility of such damage.
=========================================================================
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, see <https://www.gnu.org/licenses/>.
//=======================================================================*/
// This file contains the following functions for utilizing the Windows
//    GUI; it is #included in QSFlame.c
//       ProgramDriver();
//       InitWindowClass();
//       CreateMainWindow();
//       MainWindowProc();
//       PopupWindowProc();
//       ImageDialogProc();
//       CoefDialogProc();
//       IParDialogProc();
//       ColorDialogProc();
//       initgraphics();
//       InitOpenFile();
//       splash();
//       OpenMapFile();
//       ShowPalette();
//       writetga();
//=========================================================================
// ProgramDriver
// task: run the message loop
// returns the WPARAM value of the final WM_QUIT message
//=========================================================================
WPARAM ProgramDriver(void)
{
   MSG msg;

   for (;;)
   {
      //----- hWnd parameter must be NULL to check all windows -----
      if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
         PostMessage(g_hWnd, WM_USER, 0, 0);

	   if (GetMessage(&msg, NULL, 0, 0))
	   {
         if (!TranslateAccelerator(g_hWnd, g_hAccel, &msg))
         {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
         }
      }
      else break;
   }
   return msg.wParam;
}
//=========================================================================
// InitWindowClass
// tasks: initialize window classes
//        register the classes
// returns 0 if OK, 1 - 2 for errors
//=========================================================================
int InitWindowClass(HANDLE hInstance)
{
   WNDCLASS wc, pwc, swc;

	//----- define a main window class -----
	wc.style = CS_HREDRAW | CS_VREDRAW; // redraw after move/resize
	wc.lpfnWndProc = (WNDPROC)MainWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(hInstance, "QSFLAMEICON");
	wc.hCursor = LoadCursor(NULL, IDC_CROSS);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = "QSFLAMEMENU";
	wc.lpszClassName = szWinName;

	//----- define a popup window class -----
	pwc.style = 0;
	pwc.lpfnWndProc = (WNDPROC)PopupWindowProc;
	pwc.cbClsExtra = 0;
	pwc.cbWndExtra = 0;
	pwc.hInstance = hInstance;
	pwc.hIcon = LoadIcon(hInstance, "QSFLAMEICON");
	pwc.hCursor = LoadCursor(NULL, IDC_ARROW);
	pwc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	pwc.lpszMenuName = NULL;
	pwc.lpszClassName = szPopupWinName;

	//----- define a splash window class -----
	swc.style = 0;
	swc.lpfnWndProc = (WNDPROC)PopupWindowProc;
	swc.cbClsExtra = 0;
	swc.cbWndExtra = 0;
	swc.hInstance = hInstance;
	swc.hIcon = LoadIcon(hInstance, "QSFLAMEICON");
	swc.hCursor = LoadCursor(NULL, IDC_ARROW);
	swc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	swc.lpszMenuName = NULL;
	swc.lpszClassName = szSplashWinName;

	//----- register the classes -----
	if (!RegisterClass(&wc)) return 1;
	if (!RegisterClass(&pwc)) return 2;
   if (RegisterClass(&swc)) oksplash = TRUE;     // enables splash window
   return 0;
}
//=========================================================================
// CreateMainWindow
// tasks: save the instance handle in a global variable
//        load the accelerator key table
//        get screen dimensions
//        initialize various GUI items:
//           get previous parameters from QSFLAME.CFG
//        create main window, initialize graphics
//        display the main program window
// returns 0 if OK, 3 - 4 for errors
//=========================================================================
int CreateMainWindow(HANDLE hInstance, int nCmdShow)
{
   //----- save the handle to the current instance -----
   g_hInstance = hInstance;

   //----- load the accelerator key table -----
	g_hAccel = LoadAccelerators(hInstance, "QSFLAMEMENU");

   //----- get screen dimensions; -----
   xscreen = GetSystemMetrics(SM_CXSCREEN);
   yscreen = GetSystemMetrics(SM_CYSCREEN);

   //----- initialize various parameters and GUI items -----
   randomize();
   initialize();
   InitOpenFile();
   InitCommonControls();

	//----- create the main window; "g_hWnd" can be local -----
	g_hWnd = CreateWindow(
      szWinName,
      "QS Flame",
      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL,
      0,                               // Default horizontal position
      0,                               // Default vertical position
		xscreen,                         // xres
		yscreen,                         // yres
      NULL,                            // Overlapped windows have no parent
      NULL,                            // Use the window class menu
      hInstance,                       // This instance owns this window
      NULL);                           // Pointer not needed

   //----- initialize graphics; -----
   //----- if window could not be created, or if graphics -----
   //-----    initialization fails, return "failure"; -----
   if (!g_hWnd) return 3;
   if (initgraphics(g_hWnd)) return 4;

   //----- stuff that only can be initialized after initgraphics() -----
   initializemore();

	//----- display the window and first image -----
	ShowWindow(g_hWnd, nCmdShow);
	UpdateWindow(g_hWnd);
   return 0;
}
//=========================================================================
// MainWindowProc
// Messages processed:
//    WM_CREATE
//    WM_CALLSPLASH    - user-defined
//    WM_PAINT
//    WM_MOVE
//    WM_SAVEIMAGE     - user-defined
//    WM_SAVEPRM            "
//    WM_COMMAND       - menu messages
//       IDM_LOADPRM
//       IDM_LOADPAL
//       IDM_SAVEPRM
//       IDM_SAVEIMAGE
//       IDM_EXIT
//       IDM_IMAGE
//       IDM_IMAGEPRM
//       IDM_COEF
//       IDM_FILTERPRM
//       IDM_NEWRAND
//       IDM_NEWIMAGE
//       IDM_APPLY
//       IDM_HELP
//       IDM_ABOUT
//    WM_DESTROY
//    WM_SIZE          - scrollbar-related messages
//    WM_HSCROLL            "
//    WM_VSCROLL            "
//    WM_KEYDOWN            "
//    WM_LBUTTONDOWN   - zoombox messages
//    WM_LBUTTONUP          "
//    WM_RBUTTONDOWN        "
//    WM_MOUSEMOVE          "
//=========================================================================
LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT message, WPARAM wParam,
									     LPARAM lParam)
{
	HWND hAboutwnd;
	HDC hdc;
	HMENU hMenu;
	HBRUSH hBrush;
	PAINTSTRUCT paintstruct;
   HCURSOR hArrow, hHourglass;
   int i, j, k;
   char string[MAX_PATH];	
   int errorcode;                      // index for error string array
   FILE *f;                            // for .PRM files
//	FILE *test;                         // to test for help file

   //----- brightness and gamma variables -----
   int offset;                         // for accessing fbuffer
   COLORREF cr;                        // for reading pixels
   int r, g, b;                        // for writing pixels
   unsigned char rch, gch, bch;        // "                "

   //----- scrolling variables -----
   static int xMaxScroll, yMaxScroll;

   //----- image resizing variables -----
   POINT saveres;                      // remember current xres/yres values

   //----- zoombox variables -----
   static RECT zoombox, oldzoombox, framezoombox;
   static int startxpos, startypos, nextxpos, nextypos;
   static int width, height;           // dimensions for erasing zoombox
   static BOOL erasebox;
   double tempscale[2];

	switch(message)
	{
		case WM_CREATE:
			if (oksplash)splash(hWnd, 1);
			
			//----- get client window max coordinates into -----
			//-----    ClientRes.right and ClientRes.bottom; -----
			GetClientRect(hWnd, &ClientRes);

			//----- make a compatible virtual memory image device, paint -----
			//-----    it black and select the logical palette for it -----
			hdc = GetDC(hWnd);
			g_vhdc = CreateCompatibleDC(hdc);
			g_hBitmap = CreateCompatibleBitmap(hdc, xres, yres);
         if (g_hBitmap == NULL)
         {
            ReleaseDC(hWnd, hdc);
            MessageBox(hWnd, "Unable to allocate virtual screen buffer.\n"
                             "Try to free up some memory.",
                             "Memory Problem", MB_OK | MB_ICONEXCLAMATION);
            PostMessage(hWnd, WM_DESTROY, 0, 0);
         }
			SelectObject(g_vhdc, g_hBitmap);
			hBrush = CreateSolidBrush(back);
			SelectObject(g_vhdc, hBrush);
			PatBlt(g_vhdc, 0, 0, xres, yres, PATCOPY);
			DeleteObject(hBrush);

			//----- make a 296 x 100 virtual hdc to erase -----
         //-----    palette previews -----
			g_phdc = CreateCompatibleDC(hdc);
			g_phBitmap = CreateCompatibleBitmap(hdc, 296, 100);
			SelectObject(g_phdc, g_phBitmap);
			hBrush = GetStockObject(BLACK_BRUSH);
         SelectObject(g_phdc, hBrush);
         PatBlt(g_phdc, 0, 0, 296, 100, PATCOPY);
			ReleaseDC(hWnd, hdc);

         //----- initialize variables for scrolling -----
         si.cbSize = sizeof(SCROLLINFO);
         si.nMin = 0;

			PostMessage(hWnd, WM_COMMAND, IDM_NEWIMAGE, 0);
			break;

		case WM_PAINT:
			hdc = BeginPaint(hWnd, &paintstruct);
			BitBlt(hdc, paintstruct.rcPaint.left, paintstruct.rcPaint.top,
					 paintstruct.rcPaint.right - paintstruct.rcPaint.left,
					 paintstruct.rcPaint.bottom - paintstruct.rcPaint.top,
					 g_vhdc, paintstruct.rcPaint.left + xCurrentScroll,
					 paintstruct.rcPaint.top + yCurrentScroll, SRCCOPY);
			EndPaint(hWnd, &paintstruct);
			break;

		case WM_MOVE:
			InvalidateRect(hWnd, NULL, 1);
			break;

		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
            case IDM_LOADPAL:
               //----- when GetOpenFileName() returns, -----
               //-----    file[] will contain the -----
               //-----    drive:\path\name.ext of selected file;-----
               //-----    filetitle[] will contain the name.ext -----
   				ofn.lpstrFile = file;
   				if (GetOpenFileName(&ofn))
               	RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE |
                                     RDW_UPDATENOW | RDW_NOCHILDREN);
               else break;
               errorcode = OpenMapFile();
               if (errorcode == 1)
               {
						MessageBox(hWnd, "Error opening MAP file",
                  			  "MAP file error", MB_OK | MB_ICONEXCLAMATION);
               	break;
               }

					//----- display and select/reject palette preview -----
               hdc = GetDC(hWnd); // save background for erasing preview
               BitBlt(g_phdc, 0, 0, 296, 100, hdc, 0, 0, SRCCOPY);
               ReleaseDC(hWnd, hdc);
               showpalette();
               errorcode = MessageBox(NULL, "Accept palette?", "Preview",
               						 	  MB_YESNO | MB_ICONINFORMATION);
               hdc = GetDC(hWnd);  // erase preview
               BitBlt(hdc, 0, 0, 296, 100, g_phdc, 0, 0, SRCCOPY);
               BitBlt(g_vhdc, 0, 0, 296, 100, g_phdc, 0, 0, SRCCOPY);
               ReleaseDC(hWnd, hdc);
               if (errorcode == IDNO)
               {
               	PostMessage(hWnd, WM_COMMAND, IDM_LOADPAL, 0);  // try again
                  break;
               }

					//----- proposed palette is accepted -----
      			for( i = 0; i < 256; i++ )
               {
               	palette[i][0] = paltemp[i][0];
               	palette[i][1] = paltemp[i][1];
               	palette[i][2] = paltemp[i][2];
					}
					setup_cmap();
               break;

            case IDM_LOADPRM:
					//----- when GetOpenFileName() returns, -----
					//-----    prmfile[] will contain the -----
					//-----    drive:\path\name.ext of selected file;-----
					//-----    prmfiletitle[] will contain the name.ext -----
					oprmn.lpstrFile = prmfile;
					if (GetOpenFileName(&oprmn))
							RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE |
								RDW_UPDATENOW | RDW_NOCHILDREN);
					else break;
					if ((f = fopen(prmfile, "rb")) != NULL)
					{
						//----- read the PRMSAVE struct into "ps" -----
                  if (fread(&ps, sizeof(PRMSAVE), 1, f) == 1)
                  {
							if (strcmp(ps.id, "QSFLAME_PARAMS"))
								MessageBox(NULL, "Invalid .FLM file",
                                         "PRM File Error",
                                         MB_OK | MB_ICONEXCLAMATION);
                     else
                     {
								if (fbufferflag)
                        {
									if (ps.prm.radius != p.radius) setup_filter(ps.prm.radius);
                        }
                        if (ps.prm.gamma != p.gamma) setup_gamma(ps.prm.gamma);

                        //----- copy "ps" values -----
                        p = ps.prm;
                        for (j = 0; j < NXFORMS; j++) x[j] = ps.x[j];

                        //----- enable proper menu items -----
                        if (oversample == 1)
                        {
									hMenu = GetMenu(hWnd);
                           if (p.brightness != 0)
										EnableMenuItem(hMenu, IDM_BRIGHT,
                                             MF_BYCOMMAND | MF_ENABLED);
                           else
										EnableMenuItem(hMenu, IDM_BRIGHT,
                                             MF_BYCOMMAND | MF_GRAYED);
                           if (p.gamma != 1.0)
										EnableMenuItem(hMenu, IDM_GAMMA,
                                             MF_BYCOMMAND | MF_ENABLED);
                           else
										EnableMenuItem(hMenu, IDM_GAMMA,
                                             MF_BYCOMMAND | MF_GRAYED);
								}
								PostMessage(hWnd, WM_COMMAND, IDM_NEWIMAGE, 0);
							}
                  }
                  else MessageBox(NULL, "Invalid QSFLAME.FLM file",
                                        "PRM File Error",
                                        MB_OK | MB_ICONEXCLAMATION);
                  fclose(f);
					}
               else
						MessageBox(g_hWnd, "Unable to open the selected "
												 "file;\n     Please try again.",
												 "FLM File Error",
												 MB_OK | MB_ICONEXCLAMATION);
               break;

            case IDM_SAVEIMAGE:
               //----- when GetSaveFileName() returns, -----
               //-----    tgafile[] will contain the -----
               //-----    drive:\path\name.ext of selected file;-----
               //-----    tgafiletitle[] will contain the name.ext -----
               stgan.lpstrFile = tgafile;
               if (GetSaveFileName(&stgan))
               	RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE |
                                     RDW_UPDATENOW);
               else break;
               errorcode = writetga(g_vhdc);
         		if (errorcode)
            		MessageBox(hWnd, saveerror[errorcode - 1],
                  			  "Save file error", MB_OK | MB_ICONEXCLAMATION);
					else
               {
               	strcpy(string, "Image saved as ");
                  strcat(string, tgafiletitle);
                  MessageBox(NULL, string, "QS Flame",
                  			  MB_OK | MB_ICONINFORMATION);
               }
               break;

            case IDM_SAVEPRM:
               //----- select a save file for a parameter set; -----
               //----- when GetSaveFileName() returns, sprmfile[] -----
               //-----    will contain the drive:\path\name.ext -----
               //-----     of selected file;-----
               //-----    sprmfiletitle[] will contain the name.ext -----
               //----- GetSaveFileName() often leaves a hole in -----
               //-----    the background, especially if it -----
               //-----    generates a message box for overwrites, -----
               //-----    so RedrawWindow() is called afterward -----
					sprmn.lpstrFile = sprmfile;
					if (GetSaveFileName(&sprmn))
					{
						RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE |
                               RDW_UPDATENOW | RDW_NOCHILDREN);

						//----- write parameter save file -----
						if ((f = fopen(sprmfile, "wb")) == NULL)
							MessageBox(g_hWnd, "Unable to open the selected file;\n"
													 "     Please try again.", "PRM File Error",
													 MB_OK | MB_ICONEXCLAMATION);
						else
						{
							strcpy(ps.id, "QSFLAME_PARAMS");
							ps.prm = p;
							for (j = 0; j < NXFORMS; j++) ps.x[j] = x[j];
							if (fwrite(&ps, sizeof(PRMSAVE), 1, f) != 1)
								MessageBox(g_hWnd, "Unable to write the selected file;\n"
														 "     Please try again.", "PRM File Error",
														 MB_OK | MB_ICONEXCLAMATION);
							else
							{
								sprintf(string, "Current parameters saved to\n     %s",
									sprmfile);
								MessageBox(g_hWnd, string, "Parameters saved", MB_OK);
							}
							fclose(f);
						}
						break;
					}
               break;

				case IDM_EXIT:
					PostMessage(hWnd,	WM_DESTROY, 0, 0);
					break;

            case IDM_IMAGE:
               //----- get new selection from dialog box -----
               saveres.x = xres;
               saveres.y = yres;
               if (DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DLG_IMAGE),
                   hWnd, (DLGPROC)ImageDialogProc) == IDCANCEL) break;

               //----- delete virtual screen and make a new one -----
               DeleteDC(g_vhdc);
			      hdc = GetDC(hWnd);
			      g_vhdc = CreateCompatibleDC(hdc);
			      g_hBitmap = CreateCompatibleBitmap(hdc, xres, yres);
               if (g_hBitmap == NULL)
               {
                  ReleaseDC(hWnd, hdc);
                  MessageBox(hWnd, "Unable to allocate virtual screen buffer.\n"
                                   "Try to free up some memory.",
                             "Memory Problem", MB_OK | MB_ICONEXCLAMATION);

                  //----- restore previous virtual screen if necessary -----
                  xres = saveres.x;
                  yres = saveres.y;
                  g_hBitmap = CreateCompatibleBitmap(hdc, xres, yres);
               }
               xos = xres * oversample;
               yos = yres * oversample;
			      SelectObject(g_vhdc, g_hBitmap);
					hBrush = CreateSolidBrush(back);
			      SelectObject(g_vhdc, hBrush);
			      PatBlt(g_vhdc, 0, 0, xres, yres, PATCOPY);
					DeleteObject(hBrush);
			      ReleaseDC(hWnd, hdc);

               //----- reset scrolling variables -----
               xCurrentScroll = yCurrentScroll = 0;
               GetClientRect(hWnd, &ClientRes);
               PostMessage(hWnd, WM_SIZE, SIZE_RESTORED,
                  (LPARAM)MAKELONG(ClientRes.right, ClientRes.bottom));

               //----- set linetable[] array -----
               //----- check for image buffer flag -----
               //----- free buffers and reallocate -----
					if (fbufferflag)
					{
						fbufferflag = FALSE;
						LocalFree(fbuffer);
					}
					if ((fbuffer = (unsigned char *)LocalAlloc(LPTR,
							xos * yos * 3)) != NULL)
						fbufferflag = TRUE;
					else
					{
						oversample = 1;
						xos = xres;
						yos = yres;
						wsprintf(string, "Error creating input image buffer.");
						MessageBox(hWnd, string, "Memory Allocation Error",
							MB_OK | MB_ICONSTOP);
						hMenu = GetMenu(hWnd);
						EnableMenuItem(hMenu,IDM_APPLY, MF_BYCOMMAND | MF_GRAYED);
						if (p.brightness != 0)
							EnableMenuItem(hMenu, IDM_BRIGHT, MF_BYCOMMAND | MF_ENABLED);
						else
							EnableMenuItem(hMenu, IDM_BRIGHT, MF_BYCOMMAND | MF_GRAYED);
						if (p.gamma != 1.0)
							EnableMenuItem(hMenu, IDM_GAMMA, MF_BYCOMMAND | MF_ENABLED);
						else
							EnableMenuItem(hMenu, IDM_GAMMA, MF_BYCOMMAND | MF_GRAYED);
					}
					for (j = 0; j < yos; j++) linetable[j] = j * xos;
					if (hitflag)
					{
						hitflag = FALSE;
						LocalFree(hits);
					}
					if ((hits = (short *)LocalAlloc(LPTR, xos * yos *
							sizeof(short))) != NULL)
						hitflag = TRUE;

               PostMessage(hWnd, WM_COMMAND, IDM_NEWIMAGE, 0);
					break;

            case IDM_IMAGEPRM:
               DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_IDLG),
                     hWnd, (DLGPROC)IParDialogProc);
               break;

            case IDM_COEF:
               DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_CDLG),
                     hWnd, (DLGPROC)CoefDialogProc);
               break;

            case IDM_COLORPRM:
               DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_FDLG),
                     hWnd, (DLGPROC)ColorDialogProc);
               break;

            case IDM_NEWRAND:
               random_xform(-1);
               PostMessage(hWnd, WM_COMMAND, IDM_NEWIMAGE, 0);
               break;

            case IDM_NEWIMAGE:
               okzoom = FALSE;
               hMenu = GetMenu(g_hWnd);
               if (fbufferflag)
                  EnableMenuItem(hMenu,IDM_APPLY,MF_BYCOMMAND|MF_ENABLED);
               iterate();
               clrscr = TRUE;
               okzoom = TRUE;
               reitflag = FALSE;
               if (p.scale == 1.0)
                  EnableMenuItem(hMenu, IDM_UNZOOM, MF_BYCOMMAND|MF_GRAYED);
               else
                  EnableMenuItem(hMenu, IDM_UNZOOM, MF_BYCOMMAND|MF_ENABLED);
               if (infinite) PostMessage(hWnd, WM_COMMAND, IDM_REITERATE, 0);
               break;

            case IDM_REITERATE:
               reitflag = TRUE;
               clrscr = FALSE;
               PostMessage(hWnd, WM_COMMAND, IDM_NEWIMAGE, 0);
               break;

            case IDM_INFINITE:
               infinite = 1 - infinite;
               if (infinite)
               {
                  MessageBox(hWnd, "Press a key or click a mouse button\n"
											  "   to terminate the iteration loop.",
                                   "Infinite Iterations", MB_OK);
                  PostMessage(hWnd, WM_COMMAND, IDM_REITERATE, 0);
               }
               break;

            case IDM_UNZOOM:
               hMenu = GetMenu(g_hWnd);
               EnableMenuItem(hMenu, IDM_UNZOOM, MF_BYCOMMAND|MF_GRAYED);
               p.center[0] = p.center[1] = 0.0;
               p.scale = 1.0;
               break;

            case IDM_APPLY:
               apply_filter();
               if (oversample > 1)
               {
                  hMenu = GetMenu(g_hWnd);
                  EnableMenuItem(hMenu,IDM_APPLY,MF_BYCOMMAND|MF_GRAYED);
               }
               break;

            case IDM_BRIGHT:
			      hdc = GetDC(hWnd);
               hArrow = LoadCursor(NULL, IDC_ARROW);
               hHourglass = LoadCursor(NULL, IDC_WAIT);
               SetCursor(hHourglass);
               for (j = 0; j < yres; j++)
               {
                  for (i = 0; i < xres; i++)
                  {
                     if (fbufferflag)
                     {
                        offset = (linetable[j] + i) * 3;
                        r = min((int)fbuffer[offset] + p.brightness, 255);
                        r = max(r, 0);
                        fbuffer[offset] = (unsigned char)r;
                        g = min((int)fbuffer[offset + 1] + p.brightness, 255);
                        g = max(g, 0);
                        fbuffer[offset + 1] = (unsigned char)g;
                        b = min((int)fbuffer[offset + 2] + p.brightness, 255);
                        b = max(b, 0);
                        fbuffer[offset + 2] = (unsigned char)b;
                     }
                     cr = GetPixel(g_vhdc, i, j);
                        r = min((int)GetRValue(cr) + p.brightness, 255);
                        r = max(r, 0);
                        g = min((int)GetGValue(cr) + p.brightness, 255);
                        g = max(g, 0);
                        b = min((int)GetBValue(cr) + p.brightness, 255);
                        b = max(b, 0);
                        SetPixel(g_vhdc, i, j, RGB((unsigned char)r,
                                 (unsigned char)g, (unsigned char)b));
                  }

                  //----- bit-blit to the display line-by-line -----
                  if (j >= yCurrentScroll && j < lastdisplayline)
                     BitBlt(hdc, 0, j - yCurrentScroll, ClientRes.right, 1,
                            g_vhdc, 0 + xCurrentScroll, j, SRCCOPY);
               }
               ReleaseDC(hWnd, hdc);
               SetCursor(hArrow);
               break;

            case IDM_GAMMA:
               hArrow = LoadCursor(NULL, IDC_ARROW);
               hHourglass = LoadCursor(NULL, IDC_WAIT);
               SetCursor(hHourglass);
			      hdc = GetDC(hWnd);
               for (j = 0; j < yres; j++)
               {
                  for (i = 0; i < xres; i++)
                  {
                     if (fbufferflag)
                     {
                        offset = (linetable[j] + i) * 3;
                        for (k = 0; k < 3; k++)
                           fbuffer[offset + k] = gtable[fbuffer[offset + k]];
                     }
                     cr = GetPixel(g_vhdc, i, j);
                     rch = gtable[GetRValue(cr)];
                     gch = gtable[GetGValue(cr)];
                     bch = gtable[GetBValue(cr)];
                     SetPixel(g_vhdc, i, j, RGB(rch, gch, bch));
                  }
                  //----- bit-blit to the display line-by-line -----
                  if (j >= yCurrentScroll && j < lastdisplayline)
                     BitBlt(hdc, 0, j - yCurrentScroll, ClientRes.right, 1,
                            g_vhdc, 0 + xCurrentScroll, j, SRCCOPY);
               }
               ReleaseDC(hWnd, hdc);
               SetCursor(hArrow);
               break;

            case IDM_HELP:
               MessageBox(NULL,
               	"Please refer to the included file:\n\n"
						"      QS Flame.pdf",
                  "QS Flame", MB_OK | MB_ICONINFORMATION);
            	break;

					/*sprintf(string, "Unable to open the help file:\n\n"
               					 "      QSFlame.hlp\n\n"
               					 "The contents of the file can\n"
               					 "   be found in the PDF file:\n\n"
                               "      QS Flame.pdf\n\n"
                               "Microsoft stopped supporting\n"
                               "   the HLP format after XP.\n"
                               "   You might be able to find\n"
                               "   a version of WinHlp32.exe\n"
                               "   that works on your system\n"
                               "   at the Microsoft Web site.");
					if ((test = fopen(helppath, "rb")) == NULL)
               {
               	MessageBox(NULL, string, "QS SymXaos",
                  			  MB_OK | MB_ICONINFORMATION);
						break;
               }
               fclose(test);
               if (WinHelp(hWnd, helppath, HELP_CONTENTS, 0) == FALSE)
               	MessageBox(NULL, string, "QS SymXaos",
                  			  MB_OK | MB_ICONINFORMATION);
               break; */

				case IDM_ABOUT:
					aboutflag = TRUE;  // so PopupWindowProc() knows what to do

					//----- window needs to be centered in main window -----
					hAboutwnd = CreateWindow(szPopupWinName, "",
						WS_POPUP,
						(xscreen >> 1) - 240,
						(yscreen >> 1) - 120,
						480, 240,
						hWnd, NULL, g_hInstance, NULL);
					ShowWindow(hAboutwnd, SW_SHOW);
					EnableWindow(hWnd, FALSE);   // make it modal
					break;
			}
			break;

		case WM_DESTROY:
         if (oksplash) splash(hWnd, 2);
         if (fbufferflag) LocalFree(fbuffer);
         if (hitflag) LocalFree(hits);
         DeleteObject(g_hBitmap);
         DeleteDC(g_vhdc);
 //        WinHelp(hWnd, helppath, HELP_QUIT, 0);
			PostQuitMessage(0);
			break;

		case WM_SIZE:
         //================================================================
         // This scrolling routine requires the global variables:
         //----------------------------------------------------------------
         //       int xres, yres;        // image/virtual bitmap dimensions
         //       RECT ClientRes;        // current size of client window
         //       SCROLLINFO si;
         //       int xCurrentScroll = 0, yCurrentScroll = 0;
         //----------------------------------------------------------------
         // and the local variables:
         //----------------------------------------------------------------
         //       static int xMaxScroll, yMaxScroll;
         //       static RECT statusdim; // if a  status bar is used
         //----------------------------------------------------------------
         // The additional global variable:
         //----------------------------------------------------------------
         //       int lastdisplayline;
         //----------------------------------------------------------------
         //    is useful for drawing (rather than bit-blitting) to a
         //    scrolling window.
         //================================================================
         // Somewhere, the following initialization should be done:
         //----------------------------------------------------------------
         //       si.cbSize = sizeof(SCROLLINFO);
         //       si.nMin = 0;
         //       GetClientRect(g_hStatuswnd, &statusdim);
         //----------------------------------------------------------------
         // and the WNDCLASS[EX] "style" member should be set to:
         //----------------------------------------------------------------
         //       CS_HREDRAW | CS_VREDRAW
         //----------------------------------------------------------------
         // so the window will be repainted automatically on any move or
         //    resize; therefore coding a response to WM_MOVE is not
         //    necessary.
         //================================================================
         // Whenever a new image/virtual bitmap is selected, its
         //    dimensions should be placed in "xres/yres" and the
         //    scrolling parameters should be adjusted  by adding
         //    the following code wherever the change is made:
         //----------------------------------------------------------------
         //       xCurrentScroll = yCurrentScroll = 0;
         //       GetClientRect(hWnd, &ClientRes);
         //       PostMessage(hWnd, WM_SIZE, SIZE_RESTORED,
         //         (LPARAM)MAKELONG(ClientRes.right, ClientRes.bottom));
         //================================================================
         // The WM_KEYDOWN message is used to enable the scrolling
         //    routine to respond to the usual keypresses.
         //================================================================
         // A status bar must be resized here also.
         //================================================================
         // This code employs "proportionately-sized" thumbs. To use the
         //    older-style thumbs, do not set si.nPage, and make the
         //    following changes:
         //----------------------------------------------------------------
         //       si.fMask = SIF_RANGE | SIF_POS;
         //       si.nMax = xMaxScroll;
         //       si.nMax = yMaxScroll;
         //================================================================
         ClientRes.right = LOWORD(lParam);
         ClientRes.bottom = HIWORD(lParam);
         //if (statusflag) ClientRes.bottom -= statusdim.bottom;

         xMaxScroll = max((xres - ClientRes.right), 0);
         if (xres - xCurrentScroll < ClientRes.right)
            xCurrentScroll = xMaxScroll;
         si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
         si.nMax = xres - 1;
         si.nPos = xCurrentScroll;
         si.nPage = ClientRes.right;
         SetScrollInfo(hWnd, SB_HORZ, &si, 1);

         yMaxScroll = max((yres - ClientRes.bottom), 0);
         if (yres - yCurrentScroll < ClientRes.bottom)
            yCurrentScroll = yMaxScroll;
         si.nMax = yres - 1;
         si.nPos = yCurrentScroll;
         si.nPage = ClientRes.bottom;
         lastdisplayline = ClientRes.bottom + yMaxScroll;
         SetScrollInfo(hWnd, SB_VERT, &si, 1);

         //----- status bar must be resized also -----
         //if (statusflag) SendMessage(g_hStatuswnd, WM_SIZE, wParam, lParam);

			break;

      case WM_HSCROLL:
         switch(LOWORD(wParam))
         {
            case SB_PAGELEFT:
               if (xCurrentScroll - ClientRes.right > 0)
                  xCurrentScroll -= ClientRes.right;
               else xCurrentScroll = 0;
               break;

            case SB_PAGERIGHT:
               if (xCurrentScroll + ClientRes.right < xMaxScroll)
                  xCurrentScroll += ClientRes.right;
               else xCurrentScroll = xMaxScroll;
               break;

            case SB_LINELEFT:
               if (xCurrentScroll > 0) xCurrentScroll--;
               break;

            case SB_LINERIGHT:
               if (xCurrentScroll < xMaxScroll) xCurrentScroll++;
               break;

            case SB_THUMBTRACK:
               xCurrentScroll = HIWORD(wParam);
               break;

            case SB_THUMBPOSITION:
               xCurrentScroll = HIWORD(wParam);
               break;
         }
         si.fMask = SIF_POS;
         si.nPos = xCurrentScroll;
         SetScrollInfo(hWnd, SB_HORZ, &si, 1);
         InvalidateRect(hWnd, NULL, 0);
         break;

      case WM_VSCROLL:
         switch(LOWORD(wParam))
         {
            case SB_TOP:
               if (yCurrentScroll) yCurrentScroll = 0;
               break;

            case SB_BOTTOM:
               if (yCurrentScroll < yMaxScroll) yCurrentScroll = yMaxScroll;
               break;

            case SB_PAGEUP:
               if (yCurrentScroll - ClientRes.bottom > 0)
                  yCurrentScroll -= ClientRes.bottom;
               else yCurrentScroll = 0;
               break;

            case SB_PAGEDOWN:
               if (yCurrentScroll + ClientRes.bottom < yMaxScroll)
                  yCurrentScroll += ClientRes.bottom;
               else yCurrentScroll = yMaxScroll;
               break;

            case SB_LINEUP:
               if (yCurrentScroll > 0) yCurrentScroll--;
               break;

            case SB_LINEDOWN:
               if (yCurrentScroll < yMaxScroll)
                  yCurrentScroll++;
               break;

            case SB_THUMBTRACK:
               yCurrentScroll = HIWORD(wParam);
               break;

            case SB_THUMBPOSITION:
               yCurrentScroll = HIWORD(wParam);
               break;
         }
         si.fMask = SIF_POS;
         si.nPos = yCurrentScroll;
         SetScrollInfo(hWnd, SB_VERT, &si, 1);
         InvalidateRect(hWnd, NULL, 0);
         break;

      case WM_KEYDOWN:
         switch(LOWORD(wParam))
         {
            case VK_HOME:
               SendMessage(hWnd, WM_VSCROLL, SB_TOP, 0L);
               break;

            case VK_END:
               SendMessage(hWnd, WM_VSCROLL, SB_BOTTOM, 0L);
               break;

            case VK_PRIOR:
               SendMessage(hWnd, WM_VSCROLL, SB_PAGEUP, 0L);
               break;

            case VK_NEXT:
               SendMessage(hWnd, WM_VSCROLL, SB_PAGEDOWN, 0L);
               break;

            case VK_UP:
               SendMessage(hWnd, WM_VSCROLL, SB_LINEUP, 0L);
               break;

            case VK_DOWN:
               SendMessage(hWnd, WM_VSCROLL, SB_LINEDOWN, 0L);
               break;

            case VK_LEFT:
               SendMessage(hWnd, WM_HSCROLL, SB_LINELEFT, 0L);
               break;

            case VK_RIGHT:
               SendMessage(hWnd, WM_HSCROLL, SB_LINERIGHT, 0L);
               break;
         }

         break;

      case WM_LBUTTONDOWN:
         //================================================================
         // zoombox routine
         //================================================================
         if (okzoom)    // set by IDM_NEWIMAGE, WM_LBUTTONUP and iterate()
         {
            //----- don't proceed if cursor is outside of image -----
            if (LOWORD(lParam) >= xres || HIWORD(lParam) >= yres) break;

            drawbox = TRUE;
            erasebox = FALSE;
            startxpos = LOWORD(lParam);
            startypos = HIWORD(lParam);
         }

         //----- this is true after the zoombox is dragged and the -----
         //-----    left mouse button is released -----
         if (zooming)
         {
            zooming = FALSE;
            p.center[0] = bounds.left + (bounds.right - bounds.left) *
                          ((double)(zoombox.left + zoombox.right) /
                           (double)(xres << 1));
            p.center[1] = bounds.top + (bounds.bottom - bounds.top) *
                          ((double)(zoombox.top + zoombox.bottom) /
                           (double)(yres << 1));

            //----- the more the zoombox strays from an aspect ratio -----
            //-----    of 4/3, the more disparate the calculated -----
            //-----    scale values along each axis will be; to avoid -----
            //-----    losing any desired pixels, the smaller of the -----
            //-----    two calculated scales is used -----
            tempscale[0] = p.scale * (double)xres /
                           (double)(zoombox.right - zoombox.left);
            tempscale[1] = p.scale * (double)yres /
                           (double)(zoombox.bottom - zoombox.top);
            p.scale = min(tempscale[0], tempscale[1]);

            SetWindowText(hWnd, "QS Flame");
            SendMessage(hWnd, WM_COMMAND, IDM_NEWIMAGE, 0);
         }
         break;

      case WM_LBUTTONUP:
         if ((okzoom) && (drawbox))
         {
            //----- if the box is big enough, set up for the new image -----
            if (zoombox.right - zoombox.left > 3 &&
                zoombox.bottom - zoombox.top > 3)
            {
               okzoom = drawbox = FALSE;
               zooming = TRUE;
            }

            //----- if not, erase the blip and start over -----
            else
            {
               zooming = TRUE;
               PostMessage(hWnd, WM_RBUTTONDOWN, 0, 0);
            }
         }
         break;

      case WM_RBUTTONDOWN:
         if (zooming)
         {
            //----- restore values set when image was finished -----
            okzoom = TRUE;
            drawbox = zooming = FALSE;

            //----- erase previous box; -----
            //----- add scroll offset to virtual dc -----
            hdc = GetDC(hWnd);
            BitBlt(hdc, oldzoombox.left, oldzoombox.top, width, height,
                   g_vhdc, oldzoombox.left + xCurrentScroll,
                   oldzoombox.top + yCurrentScroll, SRCCOPY);
            ReleaseDC(hWnd, hdc);
            SetWindowText(hWnd, "QS Flame");
         }
         break;

      case WM_MOUSEMOVE:
         if (okzoom)
         {
            //----- update zoombox struct, draw new box, display status -----
            if (drawbox)
            {
               nextxpos = LOWORD(lParam);
               nextypos = HIWORD(lParam);

               //----- keep zoombox from being drawn off image -----
               //-----    if image size is smaller than client window -----
               if (nextxpos >= xres) nextxpos = xres - 1;
               if (nextypos >= yres) nextypos = yres - 1;

               //----- box grows from a corner -----
               zoombox.left = min(startxpos, nextxpos);
               zoombox.top = min(startypos, nextypos);
               zoombox.right = max(startxpos, nextxpos);
               zoombox.bottom = max(startypos, nextypos);
               if (zoombox.right - zoombox.left < 1 ||
                   zoombox.bottom - zoombox.top < 1) break;

               /*----- this code uses initial mouse button press -----
               //-----    to locate center of the zoom box -----
               //----- it also preserves a 4/3 aspect ratio -----
               width = abs(startxpos - nextxpos);
               height = abs(startypos - nextypos);
               zoombox.left = startxpos - width;
               zoombox.top = startypos - height;
               zoombox.right = startxpos + width;
               zoombox.bottom = startypos + height;
               if (zoombox.right - zoombox.left < 1 ||
               zoombox.bottom - zoombox.top < 1) break;
               if (zoombox.left < 0) zoombox.left = 0;
               if (zoombox.top < 0) zoombox.top = 0;
               if (zoombox.right > xres - 1) zoombox.right = xres - 1;
               if (zoombox.bottom > yres - 1) zoombox.bottom = yres -1;

               //----- preserve 4/3 aspect ratio -----
               if (aspectflag)
               {
                  width = zoombox.right - zoombox.left;
                  height = zoombox.bottom - zoombox.top;
                  if (((double)height / (double)width) < 0.75)
                  {
                     adjustment = (int)(((0.75 * (double)width) -
                                         (double)height) / 2.0);
                     if (zoombox.top < adjustment)
                     {
                        zoombox.top = 0;
                        zoombox.bottom = height + (adjustment << 1);
                     }
                     else if (zoombox.bottom > yres - 1 - adjustment)
                     {
                        zoombox.top = (yres - 1) -
                                      (height + (adjustment << 1));
                        zoombox.bottom = yres - 1;
                     }
                     else
                     {
                        zoombox.top -= adjustment;
                        zoombox.bottom += adjustment;
                     }
                  }
                  else if (((double)height / (double)width) > 0.75)
                  {
                     adjustment = (int)(((1.333333*(double)height) -
                                         (double)width) / 2.0);
                     if (zoombox.left < adjustment)
                     {
                        zoombox.left = 0;
                        zoombox.right = width + (adjustment << 1);
                     }
                     else if (zoombox.right > xres - 1 - adjustment)
                     {
                        zoombox.left = (xres - 1) -
                                       (width + (adjustment << 1));
                        zoombox.right = xres - 1;
                     }
                     else
                     {
                        zoombox.left -= adjustment;
                        zoombox.right += adjustment;
                     }
                  }
               }
               //----- end of 4/3 aspect ratio routine -----*/

               hdc = GetDC(hWnd);
			      hBrush = GetStockObject(WHITE_BRUSH);
			      SelectObject(hdc, hBrush);

               //----- erase previous box -----
               //----- add scroll offset to virtual dc -----
               if (erasebox)
               {
                  width = oldzoombox.right - oldzoombox.left + 2;
                  height = oldzoombox.bottom - oldzoombox.top + 2;
                  BitBlt(hdc, oldzoombox.left, oldzoombox.top, width, height,
                         g_vhdc, oldzoombox.left + xCurrentScroll,
                         oldzoombox.top + yCurrentScroll, SRCCOPY);
               }
               else erasebox = TRUE;

               //----- draw new box -----
               framezoombox = zoombox;
               framezoombox.right++;   // FrameRect() stops one pixel short
               framezoombox.bottom++;  //    of the .right/.bottom values
               FrameRect(hdc, &framezoombox, hBrush);
               oldzoombox = framezoombox;
               width = oldzoombox.right - oldzoombox.left + 2;
               height = oldzoombox.bottom - oldzoombox.top + 2;

               //----- adjust zoombox to logical x/y range -----
               zoombox.left += xCurrentScroll;
               zoombox.top += yCurrentScroll;
               zoombox.right += xCurrentScroll;
               zoombox.bottom += yCurrentScroll;

               ReleaseDC(hWnd, hdc);

               //----- display x/y ratio of zoombox in caption bar -----
               sprintf(string, "x/y: %.6f", (double)(zoombox.right -
                       zoombox.left) / (double)(zoombox.bottom -
                       zoombox.top));
               SetWindowText(hWnd, string);
            }
         }
         break;

		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
//=========================================================================
// PopupWindowProc - handles save status window and About box;
//    perhaps it would have been better to do each one separately.
// Messages processed:
//    WM_CREATE
//    WM_PAINT
//    WM_SIZE
//    WM_MOVE
//    WM_COMMAND
//       BN_CLICKED     - for OK button in save window
//    WM_KEYDOWN        - to
//    WM_SYSKEYDOWN
//    WM_LBUTTONDOWN    - for closing the About window
//    WM_RBUTTONDOWN    -    "
//    WM_CHAR           - to recognize Enter in save window, or
//                           any non-Alt keypress in About window
//=========================================================================
LRESULT CALLBACK PopupWindowProc(HWND hPopupwnd, UINT message,
                                 WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT paintstruct;

	switch(message)
	{
		case WM_CREATE:
         //----- About box -----
         hdc = GetDC(hPopupwnd);
         g_vabouthdc = CreateCompatibleDC(hdc);
         g_hAboutbitmap = LoadBitmap(g_hInstance, "QSABOUTBITMAP");
         if (g_hAboutbitmap == NULL)
         {
            DeleteObject(g_hAboutbitmap);
            DeleteDC(g_vabouthdc);
            EnableWindow(g_hWnd, TRUE);
            DestroyWindow(hPopupwnd);
            MessageBox(g_hWnd, "Unable to allocate bitmap buffer.\n"
                               "Try to free up some memory.",
                       "Memory Problem", MB_OK | MB_ICONEXCLAMATION);
               break;
         }
         SelectObject(g_vabouthdc, g_hAboutbitmap);
         ReleaseDC(hPopupwnd, hdc);
			break;

		case WM_PAINT:
         hdc = BeginPaint(hPopupwnd, &paintstruct);
         BitBlt(hdc, 0, 0, 480, 240, g_vabouthdc, 0, 0, SRCCOPY);
         EndPaint(hPopupwnd, &paintstruct);
			break;

		case WM_MOVE:
		case WM_SIZE:
			InvalidateRect(hPopupwnd, NULL, 1);
			break;

      case WM_KEYDOWN:
      case WM_SYSKEYDOWN:
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
         if (aboutflag)
         {
            aboutflag = FALSE;
            DeleteObject(g_hAboutbitmap);
            DeleteDC(g_vabouthdc);
            EnableWindow(g_hWnd, TRUE);
            DestroyWindow(hPopupwnd);
         }
         break;

		default:
			return(DefWindowProc(hPopupwnd, message, wParam, lParam));
	}
	return 0;
}
//=========================================================================
// Image resolution dialog box Proc
//=========================================================================
#pragma argsused
LRESULT CALLBACK ImageDialogProc(HWND hDwnd, UINT message,
                                 WPARAM wParam, LPARAM lParam)
{
   char string[6], *endptr;
   int xnew, ynew;

   switch(message)
   {
      case WM_INITDIALOG:
			sprintf(string, "%d", xres);
			SetDlgItemText(hDwnd, IDC_IEDIT1, string);
			sprintf(string, "%d", yres);
         SetDlgItemText(hDwnd, IDC_IEDIT2, string);
         return 1;

      case WM_PARAMERROR:
         MessageBox(hDwnd, "Range: 200 - 2000",
                    "Parameter Error", MB_OK | MB_ICONEXCLAMATION);
         return 1;

      case WM_COMMAND:
         switch(LOWORD(wParam))
         {
            case IDOK:
               //----- xres -----
               GetDlgItemText(hDwnd, IDC_IEDIT1, string, 5);
               xnew = strtol(string, &endptr, 10);
               if (xnew < 200 || xnew > 2000)
               {
                  PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                  return 1;
               }

               //----- yres -----
               GetDlgItemText(hDwnd, IDC_IEDIT2, string, 5);
               ynew = strtol(string, &endptr, 10);
               if (ynew < 200 || ynew > 2000)
               {
                  PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                  return 1;
               }

               //----- specifies the value returned by DialogBox() -----
               xres = xnew;
               yres = ynew;
               EndDialog(hDwnd, IDOK);
               return 1;

            case IDCANCEL:
               EndDialog(hDwnd, IDCANCEL);
               return 1;
         }
   }
   return 0;
}
//=========================================================================
// Coefficient dialog box Proc
//=========================================================================
#pragma argsused
LRESULT CALLBACK CoefDialogProc(HWND hDwnd, UINT message,
                                WPARAM wParam, LPARAM lParam)
{
   int i, j, k;
   int array, index;
   static int error;
   char *endptr;


   switch(message)
   {
      case WM_INITDIALOG:
         //----- initialize the edit boxes -----
         array = 0;
         index = IDC_CEDIT000;
         for (i = 0; i < p.active; i++)
            for (j = 0; j < 3; j++)
               for (k = 0; k < 2; k++)
               {
                  g_hEwnd[array] = GetDlgItem(hDwnd, index);
                  EnableWindow(g_hEwnd[array++], TRUE);
                  sprintf(string, "%.9f", x[i].c[j][k]);
                  SetDlgItemText(hDwnd, index++, string);
               }
         for (; i < NXFORMS; i++)
            for (j = 0; j < 6; j++)
            {
               g_hEwnd[array] = GetDlgItem(hDwnd, index);
               sprintf(string, "0.000000000");
               SetDlgItemText(hDwnd, index++, string);
               EnableWindow(g_hEwnd[array++], FALSE);
            }

         return 1;

      case WM_PARAMERROR:
         MessageBox(hDwnd, paramerror[error], "Parameter Error",
                    MB_OK | MB_ICONEXCLAMATION);
         return 1;

      case WM_COMMAND:
         switch(LOWORD(wParam))
         {
            case IDOK:
               for (i = 0; i < NXFORMS; i++) xt[i] = x[i];
               index = IDC_CEDIT000;
               for (i = 0; i < p.active; i++)
                  for (j = 0; j < 3; j++)
                     for (k = 0; k < 2; k++)
                     {
                        GetDlgItemText(hDwnd, index++, string, 30);
                        xt[i].c[j][k] = strtod(string, &endptr);
                        if (fabs(xt[i].c[j][k]) > 1.0)
                        {
                           error = 1;
                           PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                           return 1;
                        }
                     }
               for (i = 0; i < NXFORMS; i++) x[i] = xt[i];
               EndDialog(hDwnd, IDOK);
               return 1;

            case IDCANCEL:
               EndDialog(hDwnd, IDCANCEL);
               return 1;
         }
   }
   return 0;
}
//=========================================================================
// Image parameter dialog box Proc
//=========================================================================
#pragma argsused
LRESULT CALLBACK IParDialogProc(HWND hDwnd, UINT message,
                                WPARAM wParam, LPARAM lParam)
{
   int i, j;
   int index, varindex, count;
   HMENU hMenu;
   COLORREF cr;
   static int tactive;
   static double tdensity;
   double distance;
   DRECT tbounds;
   char *endptr;
   static int error;

   switch(message)
   {
      case WM_INITDIALOG:
         //----- enable everything in the 6 set group boxes -----
         //----- enable oversample radio buttons -----
         tactive = p.active;
         index = IDC_IRB1;
         for (i = 0; i < 48; i++)
         {
            g_hEwnd[i] = GetDlgItem(hDwnd, index++);
            EnableWindow(g_hEwnd[i], TRUE);
         }
         index = IDC_IRB49;
         for (i = 48; i < 51; i++)
         {
            g_hEwnd[i] = GetDlgItem(hDwnd, index++);
            EnableWindow(g_hEwnd[i], TRUE);
         }

         //----- initialize the radio buttons -----
         //-----    "Oversampling Factor" -----
         CheckRadioButton(hDwnd, IDC_IRB49, IDC_IRB51, IDC_IRB49 +
                          oversample - 1);

         //-----    "Number of Sets" -----
         CheckRadioButton(hDwnd, IDC_IRB43, IDC_IRB48, IDC_IRB43 +
                          p.active - 1);

         //-----    formula for each active set -----
         for (i = 0; i < p.active; i++)
         {
            for (j = 0; j < 7; j++)
               if (x[i].var[j] == 1.0)
               {
                  varindex = j;
                  break;
               }
            index = IDC_IRB1 + (i << 3);
            CheckRadioButton(hDwnd, index, index + 6, index + varindex);
         }

         //----- initialize the set edit boxes -----
         for (i = 0; i < p.active; i++)
         {
            index = IDC_IEDIT1 + (i << 3);
            sprintf(string, "%.9f", x[i].density);
            SetDlgItemText(hDwnd, index, string);
         }

         //----- disable the inactive set group boxes -----
         for (i = (p.active << 3); i < 48; i++)
            EnableWindow(g_hEwnd[i], FALSE);

         //----- initialize the zoom edit boxes -----
         sprintf(string, "%.9f", p.scale);
         SetDlgItemText(hDwnd, IDC_IEDIT7, string);
         sprintf(string, "%.9f", p.center[0]);
         SetDlgItemText(hDwnd, IDC_IEDIT8, string);
         sprintf(string, "%.9f", p.center[1]);
         SetDlgItemText(hDwnd, IDC_IEDIT9, string);

         return 1;

      case WM_PARAMERROR:
         MessageBox(hDwnd, paramerror[error], "Parameter Error",
                    MB_OK | MB_ICONEXCLAMATION);
         return 1;

      case WM_COMMAND:
         switch(LOWORD(wParam))
         {
            case IDOK:
               //----- set formulae for each active set ------
               for (i = 0; i < NXFORMS; i++) xt[i] = x[i];
               for (i = 0; i < tactive; i++)
               {
                  index = IDC_IRB1 + (i << 3);

                  //----- read formula type for each set -----
                  for (j = 0; j < 7; j++)
                  {
                     xt[i].var[j] = 0.0;
                     if (SendDlgItemMessage(hDwnd, index + j,
                         BM_GETCHECK, 0, 0))
                     {
                        xt[i].var[j] = 1.0;
                        break;
                     }
                  }
                  if (j == 7)          // no radio button checked
                  {
                     error = 2;
                     PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                     return 1;
                  }

                  //----- read density factor -----
                  GetDlgItemText(hDwnd, index + 7, string, 30);
                  xt[i].density = strtod(string, &endptr);
                  if (xt[i].density < 0.0 || xt[i].density > 1.0)
                  {
                     error = 3;
                     PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                     return 1;
                  }
               }
               for (i = tactive; i < NXFORMS; i++) xt[i].density = 0.0;
               for (i = 0; i < NXFORMS; i++)
                  x[i] = xt[i];
               //----- end of formulae/density routine -----

               //----- set zoom values -----
               pt = p;

               //----- scale -----
               GetDlgItemText(hDwnd, IDC_IEDIT7, string, 30);
               pt.scale = strtod(string, &endptr);
               if (pt.scale < 0.1 || pt.scale > 10.0)
               {
                  error = 4;
                  PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                  return 1;
               }

               //----- center coordinates; -----
               GetDlgItemText(hDwnd, IDC_IEDIT8, string, 30);
               pt.center[0] = strtod(string, &endptr);
               GetDlgItemText(hDwnd, IDC_IEDIT9, string, 30);
               pt.center[1] = strtod(string, &endptr);

               if (pt.scale >= p.scale) tbounds = bounds;
               else
               {
                  //----- expand bounds based on new scale -----
                  distance = (p.center[0] - bounds.left) *
                             (p.scale / pt.scale);
                  tbounds.left = p.center[0] - distance;
                  tbounds.right = p.center[0] + distance;
                  distance = (p.center[1] - bounds.top) *
                             (p.scale / pt.scale);
                  tbounds.top = p.center[1] - distance;
                  tbounds.bottom = p.center[1] + distance;
               }

               if (pt.center[0] < tbounds.left ||
                   pt.center[0] > tbounds.right ||
                   pt.center[1] < tbounds.top ||
                   pt.center[1] > tbounds.bottom)
               {
                  error = 5;
                  PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                  return 1;
               }

               p = pt;
               p.active = tactive;
               //----- end of zoom routins -----

               //----- finally, attempt to set "oversample" -----
               {
                  for (i = 0; i < 3; i++)
                  {
                     if (SendDlgItemMessage(hDwnd, IDC_IRB49 + i,
                         BM_GETCHECK, 0, 0))
                     {
                        //----- don't reset it if it's the same -----
                        if (oversample == i + 1) break;

                        oversample = i + 1;
                        xos = xres * oversample;
                        yos = yres * oversample;
                        if (fbufferflag)
                        {
                           fbufferflag = FALSE;
                           LocalFree(fbuffer);
                        }

                        //----- if OK, jump to linetable[] and hit -----
                        //-----    buffer setup, then to oversample -----
                        //-----    factor setup option -----
                        if ((fbuffer = (unsigned char *)LocalAlloc(LPTR,
                                        xos * yos * 3)) != NULL)
                           fbufferflag = TRUE;

                        else
                        {
                           //----- try again with oversample == 1 -----
                           oversample = 1;
                           xos = xres;
                           yos = yres;
                           CheckRadioButton(hDwnd, IDC_IRB49,
                                            IDC_IRB51, IDC_IRB49);
                           wsprintf(string, "Error creating input image "
                                            "buffer.\n"
                                            "Oversample will be set to 1");
                           MessageBox(g_hWnd,string,"Memory Allocation Error",
                                      MB_OK | MB_ICONSTOP);

                           //----- if OK, fill fbuffer and enable -----
                           //-----    Apply Filter option -----
                           if ((fbuffer = (unsigned char *)LocalAlloc(LPTR,
                                           xos * yos * 3)) != NULL)
                           {
                              count = 0;
                              for (j = 0; j < yres; j++)
                                 for (i = 0; i < xres; i++)
                                 {
                                    cr = GetPixel(g_vhdc, i, j);
                                    fbuffer[count++] = GetRValue(cr);
                                    fbuffer[count++] = GetGValue(cr);
                                    fbuffer[count++] = GetBValue(cr);
                                 }
                              fbufferflag = TRUE;
                              hMenu = GetMenu(g_hWnd);
					               EnableMenuItem(hMenu, IDM_APPLY,
                                             MF_BYCOMMAND | MF_ENABLED);
                           }

                           //----- or else user will have to start over -----
                           else
                           {
                              wsprintf(string, "Still an error creating "
                                               "input image buffer.");
                              MessageBox(g_hWnd, string,
                                         "Memory Allocation Error",
                                         MB_OK | MB_ICONSTOP);
                              hMenu = GetMenu(g_hWnd);
					               EnableMenuItem(hMenu, IDM_APPLY,
                                             MF_BYCOMMAND | MF_GRAYED);
                           }
                        }

                        //----- linetable[] and hit buffer setup -----
                        for (j = 0; j < yos; j++) linetable[j] = j * xos;
                        if (hitflag)
                        {
                           hitflag = FALSE;
                           LocalFree(hits);
                        }
                        if ((hits = (short *)LocalAlloc(LPTR, xos * yos *
                             sizeof(short))) != NULL)
                           hitflag = TRUE;

                        //----- option to set up new oversample factor, -----
                        //-----    which will render new image and thus -----
                        //-----    enable Apply Filter option, -----
                        //----- or to postpone, causing Apply Filter -----
                        //-----    option to be disabled -----
                        if (fbuffer)
                        {
                           wsprintf(string, "   To set up the new "
                                            "oversampling factor, a new\n"
                                            "image must be rendered, "
                                            "overwriting the current\n"
                                            "image. To postpone this step, "
                                            "select %cNo%c;  the\n"
                                            "new factor will not be set up "
                                            "until the next action\n"
                                            "in which a new image is "
                                            "rendered.\n"
                                            "   Proceed now?", '"', '"');
                           if (MessageBox(g_hWnd, string, "Proceed now?",
                               MB_YESNO | MB_ICONQUESTION) == IDYES)
                              PostMessage(g_hWnd, WM_COMMAND,
                                          IDM_NEWIMAGE, 0);
                           else
                           {
                              hMenu = GetMenu(g_hWnd);
					               EnableMenuItem(hMenu, IDM_APPLY, MF_BYCOMMAND |
                                             MF_GRAYED);
                           }
                        }
                        break;
                     }
                  }
                  hMenu = GetMenu(g_hWnd);
                  if (p.brightness != 0)
                     EnableMenuItem(hMenu, IDM_BRIGHT,
                                    MF_BYCOMMAND | MF_ENABLED);
                  else
                     EnableMenuItem(hMenu, IDM_BRIGHT,
                                    MF_BYCOMMAND | MF_GRAYED);
                  if (p.gamma != 1.0)
                     EnableMenuItem(hMenu, IDM_GAMMA,
                                    MF_BYCOMMAND | MF_ENABLED);
                  else
                     EnableMenuItem(hMenu, IDM_GAMMA,
                                    MF_BYCOMMAND | MF_GRAYED);
               }
               //----- end of "oversample" routine -----

               EndDialog(hDwnd, IDOK);
               return 1;

            case IDCANCEL:
               EndDialog(hDwnd, IDCANCEL);
               return 1;

            case IDC_RANDOM:
               PostMessage(g_hWnd, WM_COMMAND, IDM_NEWRAND, 0);
               EndDialog(hDwnd, IDC_RANDOM);
               return 1;

            case IDC_IRB43:
            case IDC_IRB44:
            case IDC_IRB45:
            case IDC_IRB46:
            case IDC_IRB47:
            case IDC_IRB48:
               for (j = 0; j < NXFORMS; j++) xt[j] = x[j];

               //----- process "number of active sets" radio buttons -----
               tactive = (LOWORD(wParam) - IDC_IRB43 + 1);
               tdensity = 1.0 / (double)tactive;

               //----- enable the active set group boxes -----
               for (i = 0; i < tactive; i++)
               {
                  index = IDC_IEDIT1 + (i << 3);
                  for (j = 0; j < 8; j++)
                     EnableWindow(g_hEwnd[(i << 3) + j], TRUE);
                  sprintf(string, "%.9f", tdensity);
                  SetDlgItemText(hDwnd, index, string);

                  //----- formula for each active set -----
                  for (j = 0; j < 7; j++)
                     if (xt[i].var[j] == 1.0)
                     {
                        varindex = j;
                        break;
                     }
                  index = IDC_IRB1 + (i << 3);
                  CheckRadioButton(hDwnd, index, index + 6,
                                   index + varindex);
               }

               //----- disable the inactive set group boxes -----
               for (i = tactive; i < NXFORMS; i++)
               {
                  index = IDC_IEDIT1 + (i << 3);
                  SetDlgItemText(hDwnd, index, "0.000000000");
                  for (j = 0; j < 8; j++)
                     EnableWindow(g_hEwnd[(i << 3) + j], FALSE);
               }
               return 1;
         }
   }
   return 0;
}
//=========================================================================
// Color/filter parameter dialog box Proc
//=========================================================================
#pragma argsused
LRESULT CALLBACK ColorDialogProc(HWND hDwnd, UINT message,
                                 WPARAM wParam, LPARAM lParam)
{
   HMENU hMenu;
   int i;
   int tbid[2] = {IDC_FTB1, IDC_FTB2};
   int editid[2] = {IDC_FEDIT1, IDC_FEDIT2};
   static HWND hTrackwnd[2];
   static int trackpos[2];
   int tblow[2] = {1, 0};
   int tbhigh[2] = {100, 200};
   int tbshift[2] = {0, 100};
   char *endptr;
   static int error;

   switch(message)
   {
      case WM_INITDIALOG:
         //----- initialize trackbars -----
         trackpos[0] = p.radius;
         trackpos[1] = p.brightness + 100;
         for (i = 0; i < 2; i++)
         {
            hTrackwnd[i] = GetDlgItem(hDwnd, tbid[i]);
            EnableWindow(hTrackwnd[i], TRUE);
            SendMessage(hTrackwnd[i], TBM_SETRANGE, (WPARAM)0,
                        (LPARAM)MAKELONG(tblow[i], tbhigh[i]));
            SendMessage(hTrackwnd[i], TBM_SETPOS, (WPARAM)1,
                        (LPARAM)trackpos[i]);
            SetDlgItemInt(hDwnd, editid[i],
                          (UINT)(trackpos[i] - tbshift[i]), 1);
         }
         if (!fbufferflag) EnableWindow(hTrackwnd[0], FALSE);

         //----- initialize the edit boxes -----
         sprintf(string, "%.9f", p.gamma);
         SetDlgItemText(hDwnd, IDC_FEDIT3, string);
         sprintf(string, "%d", rback);
         SetDlgItemText(hDwnd, IDC_FEDIT4, string);
         sprintf(string, "%d", gback);
         SetDlgItemText(hDwnd, IDC_FEDIT5, string);
         sprintf(string, "%d", bback);
         SetDlgItemText(hDwnd, IDC_FEDIT6, string);

         return 1;

      case WM_HSCROLL:
         for (i = 0; i < 2; i++)
         {
            if (hTrackwnd[i] == (HWND)lParam)
            {
               switch(LOWORD(wParam))
               {
                  case TB_TOP:
                  case TB_BOTTOM:
                  case TB_LINEDOWN:
                  case TB_LINEUP:
                  case TB_THUMBPOSITION:
                  case TB_THUMBTRACK:
                  case TB_PAGEUP:
                  case TB_PAGEDOWN:
                     trackpos[i] = SendMessage(hTrackwnd[i], TBM_GETPOS,
                                               0, 0);
               }
               SetDlgItemInt(hDwnd, editid[i],
                             (UINT)(trackpos[i] - tbshift[i]), 1);
               return 1;
            }
         }
         break;

      case WM_PARAMERROR:
         MessageBox(hDwnd, paramerror[error], "Parameter Error",
                    MB_OK | MB_ICONEXCLAMATION);
         return 1;

      case WM_COMMAND:
         switch(LOWORD(wParam))
         {
            case IDOK:
               pt = p;

               GetDlgItemText(hDwnd, IDC_FEDIT3, string, 30);
               pt.gamma = strtod(string, &endptr);
               if (pt.gamma < 0.1 || pt.gamma > 10.0)
               {
                  error = 6;
                  PostMessage(hDwnd, WM_PARAMERROR, 0, 0);
                  return 1;
               }
 
               GetDlgItemText(hDwnd, IDC_FEDIT4, string, 30);
               rback = strtol(string, &endptr, 10);
               if (rback < 0) rback = 0;
					if (rback > 255) rback = 255;

               GetDlgItemText(hDwnd, IDC_FEDIT5, string, 30);
               gback = strtol(string, &endptr, 10);
               if (gback < 0) gback = 0;
					if (gback > 255) gback = 255;

               GetDlgItemText(hDwnd, IDC_FEDIT6, string, 30);
               bback = strtol(string, &endptr, 10);
               if (bback < 0) bback = 0;
					if (bback > 255) bback = 255;

					back = RGB(rback, gback, bback);
					
               pt.radius = trackpos[0];
               pt.brightness = trackpos[1] - 100;

               if (pt.gamma != p.gamma) setup_gamma(pt.gamma);
               if (fbufferflag)
                  if (pt.radius != p.radius) setup_filter(pt.radius);

               p = pt;

               hMenu = GetMenu(g_hWnd);
               if (p.brightness != 0)
                  EnableMenuItem(hMenu, IDM_BRIGHT, MF_BYCOMMAND |
                                  MF_ENABLED);
               else
                  EnableMenuItem(hMenu, IDM_BRIGHT, MF_BYCOMMAND |
                                 MF_GRAYED);
               if (p.gamma != 1.0)
                  EnableMenuItem(hMenu, IDM_GAMMA, MF_BYCOMMAND |
                                 MF_ENABLED);
               else
                  EnableMenuItem(hMenu, IDM_GAMMA, MF_BYCOMMAND |
                                 MF_GRAYED);

               EndDialog(hDwnd, IDOK);
               return 1;

            case IDCANCEL:
               EndDialog(hDwnd, IDCANCEL);
               return 1;
         }
   }
   return 0;
}
//=========================================================================
// Initialize graphics
// Checks for 24-bit or higher color resolution
// Returns: 0 for true color
//          1 if lesser graphics capability
//=========================================================================
int initgraphics(HWND hWnd)
{
	HDC hdc;
   int returnvalue;

	hdc = GetDC(hWnd);
   if (GetDeviceCaps(hdc, BITSPIXEL) >= 24) returnvalue = 0;
	else returnvalue = 1;
	ReleaseDC(hWnd, hdc);
   return returnvalue;
}

//=========================================================================
// Initialize the File Open parameters
//=========================================================================
void InitOpenFile(void)
{
   GetCurrentDirectory(MAX_PATH, tgafile);
   strcpy(prmfile, tgafile);
   strcpy(sprmfile, tgafile);
   strcat(tgafile, "\\*.tga");
   strcat(prmfile, "\\*.flm");
   strcat(sprmfile, "\\*.flm");

   //----- initialize the OPENFILENAME struct -----
   ofn.lStructSize = sizeof(OPENFILENAME);
   ofn.hwndOwner = NULL;
   ofn.hInstance = NULL;
   ofn.lpstrFilter = filter;
   ofn.lpstrCustomFilter = NULL;
   ofn.nMaxCustFilter = 0;
   ofn.nFilterIndex = 1;
   ofn.lpstrFile = file;
   ofn.nMaxFile = sizeof(file);
   ofn.lpstrFileTitle = filetitle;
   ofn.nMaxFileTitle = sizeof(filetitle);
   ofn.lpstrInitialDir = NULL;
   ofn.lpstrTitle = title;
   ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
               OFN_HIDEREADONLY;
   ofn.nFileOffset = 0;
   ofn.nFileExtension = 0;
   ofn.lpstrDefExt = defext;
   ofn.lCustData = NULL;
   ofn.lpfnHook = NULL;
   ofn.lpTemplateName = NULL;

   //----- initialize the OPENFILENAME struct -----
   oprmn.lStructSize = sizeof(OPENFILENAME);
   oprmn.hwndOwner = NULL;
   oprmn.hInstance = NULL;
   oprmn.lpstrFilter = prmfilter;
   oprmn.lpstrCustomFilter = NULL;
   oprmn.nMaxCustFilter = 0;
   oprmn.nFilterIndex = 1;
   oprmn.lpstrFile = prmfile;
   oprmn.nMaxFile = sizeof(prmfile);
   oprmn.lpstrFileTitle = prmfiletitle;
   oprmn.nMaxFileTitle = sizeof(prmfiletitle);
   oprmn.lpstrInitialDir = NULL;
   oprmn.lpstrTitle = prmtitle;
   oprmn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST |
               OFN_HIDEREADONLY;
   oprmn.nFileOffset = 0;
   oprmn.nFileExtension = 0;
   oprmn.lpstrDefExt = prmdefext;
   oprmn.lCustData = NULL;
   oprmn.lpfnHook = NULL;
   oprmn.lpTemplateName = NULL;

   //----- initialize the OPENFILENAME struct -----
   sprmn.lStructSize = sizeof(OPENFILENAME);
   sprmn.hwndOwner = NULL;
   sprmn.hInstance = NULL;
   sprmn.lpstrFilter = sprmfilter;
   sprmn.lpstrCustomFilter = NULL;
   sprmn.nMaxCustFilter = 0;
   sprmn.nFilterIndex = 1;
   sprmn.lpstrFile = sprmfile;
   sprmn.nMaxFile = sizeof(sprmfile);
   sprmn.lpstrFileTitle = sprmfiletitle;
   sprmn.nMaxFileTitle = sizeof(sprmfiletitle);
   sprmn.lpstrInitialDir = NULL;
   sprmn.lpstrTitle = sprmtitle;
   sprmn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
               OFN_HIDEREADONLY;
   sprmn.nFileOffset = 0;
   sprmn.nFileExtension = 0;
   sprmn.lpstrDefExt = sprmdefext;
   sprmn.lCustData = NULL;
   sprmn.lpfnHook = NULL;
   sprmn.lpTemplateName = NULL;

   //----- initialize the OPENFILENAME struct -----
   stgan.lStructSize = sizeof(OPENFILENAME);
   stgan.hwndOwner = NULL;
   stgan.hInstance = NULL;
   stgan.lpstrFilter = tgafilter;
   stgan.lpstrCustomFilter = NULL;
   stgan.nMaxCustFilter = 0;
   stgan.nFilterIndex = 1;
   stgan.lpstrFile = tgafile;
   stgan.nMaxFile = sizeof(tgafile);
   stgan.lpstrFileTitle = tgafiletitle;
   stgan.nMaxFileTitle = sizeof(tgafiletitle);
   stgan.lpstrInitialDir = NULL;
   stgan.lpstrTitle = tgatitle;
   stgan.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
                 OFN_HIDEREADONLY;
   stgan.nFileOffset = 0;
   stgan.nFileExtension = 0;
   stgan.lpstrDefExt = tgadefext;
   stgan.lCustData = NULL;
   stgan.lpfnHook = NULL;
   stgan.lpTemplateName = NULL;
}
//=========================================================================
// Put up a brief splash window, determined by value of "index"
// At startup, display GNU message
//=========================================================================
int splash(HWND hWnd, int index)
{
	HWND hSplashwnd;
	HDC hdc, memhdc, clienthdc;
	HBITMAP hBitmap;
   HBRUSH hBrush;
   int i, j;

	hSplashwnd = CreateWindow(szSplashWinName, "", WS_POPUP,
									  (xscreen >> 1) - 160,
									  (yscreen >> 1) - 160, 320, 320,
									  hWnd, NULL, g_hInstance, NULL);
	EnableWindow(hWnd, FALSE);
	ShowCursor(FALSE);
	ShowWindow(hSplashwnd, SW_SHOW);

   //----- clear client window for concluding image -----
   if (index == 2)
   {
      clienthdc = GetDC(g_hWnd);
      hBrush = GetStockObject(BLACK_BRUSH);
      SelectObject(clienthdc, hBrush);
      PatBlt(clienthdc, 0, 0, xscreen, yscreen, PATCOPY);
      ReleaseDC(g_hWnd, clienthdc);
   }

   //----- load internal bitmap -----
   if (index == 1)
      hBitmap = LoadBitmap(g_hInstance, "QSSPLASH1BITMAP");
   else
      hBitmap = LoadBitmap(g_hInstance, "QSSPLASH2BITMAP");
	hdc = GetDC(hSplashwnd);
	memhdc = CreateCompatibleDC(hdc);
	SelectObject(memhdc, hBitmap);

   //----- show splash image in popup window and clean up -----
	BitBlt(hdc, 0, 0, 320, 320, memhdc, 0, 0, SRCCOPY);
	DeleteObject(hBitmap);
	if (index == 1) Sleep(3000);
	else Sleep(2000);
	EnableWindow(hWnd, TRUE);
	ShowCursor(TRUE);
	DestroyWindow(hSplashwnd);
	ReleaseDC(hSplashwnd, hdc);
	DeleteDC(memhdc);

   //----- generate the default palette for initial images -----
   if (index == 1)
   {
      for (i = 0; i < 256; i++)
         for (j = 0; j < 3; j++)
            palette[i][j] = fpalette[i][j];
		setup_cmap();
		sprintf(string,
            "QS Flame: Copyright (C) 1999 - 2018 Michael Sargent\n\n"
            "This program is free software. You are welcome to redistribute\n"
            "   it under certain conditions. It comes with ABSOLUTELY NO\n"
            "   WARRANTY. For details, refer to the GNU General Public\n"
				"   License in the included PDF file.");
         MessageBox(NULL, string, "      GNU's Not UNIX", MB_OK);
   }
	return 0;
}
//=========================================================================
// Open the selected .map file; set palette in paltemp[][]
// Returns 0 if OK; 1 if file error
//=========================================================================
int OpenMapFile(void)
{
   int i, r, g, b; //j, b, color, index;
   char buffer[100], filetemp[MAX_PATH];
   FILE *map;

   //----- open the file and reset the palette[][] array -----
   strcpy(filetemp, file);
   CharUpper(filetemp);
   CharToOem(filetemp, filetemp);
   if ((map = fopen(filetemp, "rt")) != NULL)
   {
      for( i = 0; i < 256; i++ )
      {
         if (fgets(buffer, 100, map) == NULL) break;
         if (sscanf(buffer, "%d %d %d", &r, &g, &b) != 3) return 1;

         paltemp[i][0] = (unsigned char)r;
         paltemp[i][1] = (unsigned char)g;
         paltemp[i][2] = (unsigned char)b;
      }
      fclose(map);
      while (i < 256) paltemp[i][0] = paltemp[i][1] = paltemp[i++][2] = 1;
      return 0;
   }
   else return 1;
}
//=========================================================================
// Display a preview of the MAP file palette selected from the dialog box
//=========================================================================
int showpalette(void)
{
	HDC hdc, previewhdc;
	HBITMAP previewbitmap;
   HPEN hPen;
   HBRUSH hBrush;
   POINT frame[5];
   LOGBRUSH logbrush;
	int i;

	hdc = GetDC(g_hWnd);

   //----- make a 296 x 100 virtual hdc to display the previews -----
   hBrush = GetStockObject(GRAY_BRUSH);
   previewhdc = CreateCompatibleDC(hdc);
   previewbitmap = CreateCompatibleBitmap(hdc, 296, 100);
   SelectObject(previewhdc, previewbitmap);
   SelectObject(previewhdc, hBrush);
   PatBlt(previewhdc, 0, 0, 296, 100, PATCOPY);

	//----- draw frame -----
	hdc = GetDC(g_hWnd);
	logbrush.lbStyle = BS_SOLID;
   logbrush.lbColor = back;
   logbrush.lbHatch = NULL;
   hBrush = CreateBrushIndirect(&logbrush);
   for (i = 0; i < 3; i++)
   {
   	frame[0].x = frame[4].x = i; frame[0].y = frame[4].y = i;
      frame[1].x = 295 - i; frame[1].y = i;
      frame[2].x = 295 - i; frame[2].y = 99 - i;
      frame[3].x = i; frame[3].y = 99 - i;
      if (i == 1)
      	hPen = CreatePen(PS_SOLID, 1, RGB(215, 225, 235)); // highlight
      else
      	hPen = CreatePen(PS_SOLID, 1, RGB(205, 215, 225));
      SelectObject(previewhdc, hPen);
      Polyline(previewhdc, frame, 5);
      DeleteObject(hPen);
	}
   SelectObject(previewhdc, hBrush);
   PatBlt(previewhdc, 3, 3, 290, 94, PATCOPY);

   //----- draw proposed palette spectrum -----
   for (i = 0; i < 256; i++)
	{
   	hPen = CreatePen(PS_SOLID, 1, RGB(paltemp[i][0], paltemp[i][1],
      	paltemp[i][2]));
      SelectObject(previewhdc, hPen);
   	MoveToEx(previewhdc, i + 20, 25, NULL);
      LineTo(previewhdc, i + 20, 75);
      DeleteObject(hPen);
   }
   BitBlt(hdc, 0, 0, min(296, ClientRes.right), min(100, ClientRes.bottom),
      	 previewhdc, 0, 0, SRCCOPY);

   ReleaseDC(g_hWnd, hdc);
	DeleteDC(previewhdc);
	DeleteObject(previewbitmap);
	return 0;
}
//=========================================================================
// Open the file & set the header; read & write the image data;  note that
//    the TARGA format writes the color bytes in reverse order:
//    blue, green, red
// Returns: 0 for success
//          1 for file opening error
//          2 for memory allocation error
//          3 for file writing error
// Presumes 24-bit True Color;
// Presumes that most likely the hdc will be a virtual bitmap.
//=========================================================================
int writetga(HDC hdc)
{
   HCURSOR cross, hourglass;
   struct TGAHEADER
   {
      char idfield;          // ID field size; located after header;
                             //    max 255
      char colormaptype;     // 0 for RGB/greyscale; 1 if present
      char imagetype;        // 1: colormapped
                             // 2: uncompressed RGB
                             // 3: uncompressed greyscale
                             // add 8 for RLE compressed
      WORD colormapstart;    // 0 for no palette
      WORD colormaplength;   // 0 for no palette
      char colormapbits;     // 0 for no palette
      WORD xstart;           // offset from upper left corner of screen
      WORD ystart;           // offset from upper left corner of screen
      WORD width;            // usually will be xres
      WORD height;           // usually will be yres
      char bits;             // bits per pixel
      char descriptor;       // bits 0 - 3: # of attribute bits/pixel
                             // bit 4: L/R: 0: normal; 1: reversed
                             // bit 5: up/down: 0: upside-down; 1: normal
                             // bits 6 -7: interlacing: 0: none
                             // usage: 00: L->R, upside down
                             //        10: R->L, upside down
                             //        20: L->R, right side up *
                             //        30: R->L, right side up
   };
   struct TGAHEADER th;
   struct TGAFOOTER
   {
      UINT extoffset;        // location of extension area
      UINT devdiroffset;     // location of developer directory
      char sig[18];
   };
   struct TGAFOOTER tf;
   COLORREF cr;              // returned by GetPixel()
   FILE *savetga;            // destination file for TGA stream
   int i, j, offset, linebytes;
   unsigned char *linebuffer;

   if ((savetga = fopen(tgafile, "wb")) == NULL)  // Can't open a file
      return 1;

   th.idfield = 0;
   th.colormaptype = 0;
   th.imagetype = 2;
   th.colormapstart = 0;
   th.colormaplength = 0;
   th.colormapbits = 0;
   th.xstart = 0;
   th.ystart = 0;
   th.width = (WORD)xres;
   th.height = (WORD)yres;
   th.bits = 24;
   th.descriptor = 0x20;

   tf.extoffset = 0;
   tf.devdiroffset = 0;
   strcpy(tf.sig, "TRUEVISION-XFILE.");
   tf.sig[17] = 0;

   linebytes = xres * 3;

   if ((linebuffer = (unsigned char *)LocalAlloc(LPTR, linebytes)) == NULL)
      return 2;

   if (fwrite(&th, sizeof(th), 1, savetga) != 1)
   {
      LocalFree(linebuffer);
      fclose(savetga);
      return 3;
   }

   //----- drawing takes some time -----
   hourglass = LoadCursor(NULL, IDC_WAIT);
   cross = SetCursor(hourglass);
   EnableWindow(g_hWnd, FALSE);

   for (j = 0; j < yres; j++)
   {
      for (i = 0; i < xres; i++)
      {
	      offset = i * 3;
	      cr = GetPixel(hdc, i, j);
	      linebuffer[offset] = GetBValue(cr);
	      linebuffer[offset + 1] = GetGValue(cr);
	      linebuffer[offset + 2] = GetRValue(cr);
		}

      if (fwrite(linebuffer, linebytes, 1, savetga) != 1)
      {
         LocalFree(linebuffer);
         fclose(savetga);
         EnableWindow(g_hWnd, TRUE);
         SetCursor(cross);
         return 3;
      }
   }

   LocalFree(linebuffer);
   fwrite(&tf, sizeof(tf), 1, savetga);
   fclose(savetga);
   EnableWindow(g_hWnd, TRUE);
   SetCursor(cross);
   return 0;
}
